package wsi.resolve;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.rygh.core.html.table.HtmlTable;
import org.rygh.core.html.table.TableData;
import org.rygh.core.html.table.TableTuple;
import org.rygh.core.model.common.MapList;
import org.rygh.core.util.counter.CounterTuple;
import org.rygh.core.util.counter.FrequencyCounter;
import org.rygh.core.util.counter.SimpleCounter;
import org.rygh.semantic.gui.swing.input.Input;

import wsi.dataguide.tree.WSXmlLeaf;
import wsi.match.MatchProperty;
import wsi.subgraph.entity.ResolvePair;
import wsi.subgraph.tree.EntityLeaf;
import wsi.subgraph.tree.EntityTree;

/**
 * <b>Partial Completeness Assumption</b><br>
 * <br>
 * 
 * An atom is a fact with variables. A Horn rule consists of a head and a body.
 * <b>B</b> is a set of atoms and the head contains one atom. We assume that the
 * rule is closed (each variable appears at least twice.)<br>
 * <br>
 * 
 * pcaconf(B->r(x,y)) <br>
 * = N / D <br>
 * = [#(x,y):B ^ r(x,y)] / [#(x,y): B ^ r(x,y')]<br>
 * <br>
 * 
 * #(x,y) : A<br>
 * the number of pairs (x,y) that fulfill A <br>
 * <br>
 * 
 * N = #(x,y) : B ^ r(x,y) <br>
 * the number of <b>distinct</b> pairs of subjects and objects in the head of
 * all instantiations that appear in the KB.<br>
 * <br>
 * 
 * D = #(x,y) : B ^ r(x,y')<br>
 * a KB knows either all or none of the r-attributes of some x. the set of facts
 * of which we know they are true and we assume they are <b>false</b> (count as
 * counter-example for a rule B->r(x,y) only those instances x that have r
 * relations but not r(x,y)). if the head atom of the rule is r(x,y), then this
 * set is just the set of facts {r(x,y') : r(x,y') in KB}.<br>
 * <br>
 * 
 * based on the above analysis, given a path match (xpath, pred_path), it can be
 * viewed as pred_path->xpath(x,y).<br>
 * <br>
 * 
 * previously in out factItemCounter, it counts the number of matches (before
 * compensation), normalized by the number of all subtrees.<br>
 * <br>
 * 
 * Thus the N part is the number of matched pairs (the same with the counter of
 * factItemCounter). the M part should be counted as the number of resolve pairs
 * (subtree, center_entity) where the subtree has the xpath and the center
 * entity has the pred_path. (thus lower than the number of subtrees)<br>
 * <br>
 * 
 * if we compute the M part as the number of subtrees where the subtree has the
 * xpath only and do not take into consideration of the center entity, then this
 * is equal to the "head coverage" as in the AMIE paper.<br>
 * <br>
 * 
 * if we compute the M part as the number of entities where the entity has the
 * pred_path, then this is equal to the "standard confidence" as in the AMIE
 * paper.<br>
 * <br>
 * 
 * @author nanmus
 * @version 13-12-07
 */
public class PCAConfidence {

	private final FrequencyCounter<String> pcaCounter = new FrequencyCounter<>();

	private SimpleCounter<String> factItemCounter;

	MapList<Input, ResolvePair> allMaxPairList;
	MapList<Input, ResolvePair> oneMaxPairList;

	private MapList<Input, WSXmlLeaf> subrootMapList;
	private Map<Input, EntityTree> entityTreeMap;

	private MatchProperty matchProperty;

	public PCAConfidence() {

	}

	public void compute() {
		FactItem item;
		int c, pcaCount = 0;

		for (CounterTuple<String> t : factItemCounter.valueSet()) {
			item = (FactItem) t.getValue();

			switch (matchProperty.getType()) {
			case EXISTS:
				pcaCount = countExistsMatch(item);
				break;

			case FORALL:
				pcaCount = countForallMatch(item);
				break;
			}

			c = t.getCount();

			pcaCounter.set(t.getKey(), item, c, pcaCount);
			item.setPcaConfidence(1. * c / pcaCount);
		}
	}

	private int countExistsMatch(FactItem item) {
		List<WSXmlLeaf> subroots;
		WSXmlLeaf subroot;
		Input input;

		EntityTree etree;

		int c, index;

		c = 0;
		index = matchProperty.getSubtreeIndex();

		for (Entry<Input, List<WSXmlLeaf>> e : subrootMapList.entrySet()) {
			input = e.getKey();
			subroots = e.getValue();

			if (subroots.size() < index)
				continue;

			subroot = subroots.get(index - 1);
			assert subroot.getXpath().getLastIndex() == index : String.format(
					"%d != %d", subroot.getXpath().getLastIndex(), index);

			etree = entityTreeMap.get(input);

			if (etree == null) {
				System.out
						.println("[warn.pca.count.exists] no entity tree for input "
								+ input);
				continue;
			}

			if (find(item, subroot, etree))
				c++;
		}

		return c;
	}

	private int countForallMatch(FactItem item) {
		List<ResolvePair> pairs;

		EntityTree etree;
		WSXmlLeaf subroot;
		Input input;

		int c = 0;

		for (Entry<Input, List<ResolvePair>> e : oneMaxPairList.getMap()
				.entrySet()) {
			input = e.getKey();
			pairs = e.getValue();

			for (ResolvePair pair : pairs) {
				EntityLeaf node = pair.getEntityLeaf();

				if (!matchProperty.validateMatchedPath(node))
					continue;

				subroot = pair.getXmlLeaf();

				etree = entityTreeMap.get(input);

				if (etree == null) {
					System.out
							.println("[warn.instantiate] no entity tree for input "
									+ input);
					continue;
				}

				if (find(item, subroot, etree))
					c++;
			}
		}

		return c;
	}

	/**
	 * this subtree is resolved to this entity node.
	 * 
	 * @param item
	 * @param subroot
	 * @param entityNode
	 * @return
	 */
	private boolean find(FactItem item, WSXmlLeaf subroot, EntityTree entityTree) {
		WSXmlLeaf dg_leaf = item.getXmlLeaf();

		return subroot.containsLeafEndsWith(dg_leaf.getXpath())
				&& entityTree.containsNodesAtPredicatePathOf(item);
	}

	public FrequencyCounter<String> getPcaCounter() {
		return pcaCounter;
	}

	public void setAllMaxPairList(MapList<Input, ResolvePair> allMaxPairList) {
		this.allMaxPairList = allMaxPairList;
	}

	public void setEntityTreeMap(Map<Input, EntityTree> entityTreeMap) {
		this.entityTreeMap = entityTreeMap;
	}

	public void setFactItemCounter(SimpleCounter<String> factItemCounter) {
		this.factItemCounter = factItemCounter;
	}

	public void setMatchProperty(MatchProperty matchProperty) {
		this.matchProperty = matchProperty;
	}

	public void setOneMaxPairList(MapList<Input, ResolvePair> oneMaxPairList) {
		this.oneMaxPairList = oneMaxPairList;
	}

	public void setSubrootMapList(MapList<Input, WSXmlLeaf> subrootMapList) {
		this.subrootMapList = subrootMapList;
	}

	public HtmlTable toHtmlTable() {
		TableData data = new TableData();
		TableTuple tuple = new TableTuple();

		tuple.add("Input to Center Path");
		tuple.add("Center to Literal Path");
		tuple.add("XmlPath");
		tuple.add("Facts");
		tuple.add("PCA Confidence");
		tuple.add("Count");
		tuple.add("Percentage");
		tuple.add("Count");
		data.add(tuple);

		for (String key : pcaCounter.keySet()) {
			tuple = new TableTuple();
			FactItem item = (FactItem) pcaCounter.getValue(key);

			if (item.getInputToCenterPath() != null)
				tuple.add(item.getInputToCenterPath().getKey());
			else
				tuple.add("NULL");

			int n, d;

			n = pcaCounter.get(key);
			d = pcaCounter.getTotal(key);

			tuple.add(item.getCenterToValulePath().getKey());
			tuple.add(item.getXmlLeaf().getXpath().getValue());
			tuple.add(item.getFactGroup().toString("<br>"));
			tuple.add(String.format("%.4f", 1. * n / d));
			tuple.add(String.format("%d / %d", n, d));
			tuple.add(String.format("%.4f", item.getPercentage()));
			tuple.add(String.format("%d / %d", factItemCounter.get(key),
					factItemCounter.getNormalizeFactor()));

			data.add(tuple);
		}

		data.sort(4);

		return new HtmlTable("Fact Item PCA Confidence", data, false);
	}
}
